import $ from '../../jquery/jquery';


var $window = $(window);
var $document = $(document);
var isTouch = 'createTouch' in document;
var html = document.documentElement;
var isIE6 = !('minWidth' in html.style);
var isLosecapture = !isIE6 && 'onlosecapture' in html;
var isSetCapture = 'setCapture' in html;


var types = {
    start: isTouch ? 'touchstart' : 'mousedown',
    over: isTouch ? 'touchmove' : 'mousemove',
    end: isTouch ? 'touchend' : 'mouseup'
};


var getEvent = isTouch ? function (event) {
    if (!event.touches) {
        event = event.originalEvent.touches.item(0);
    }
    return event;
} : function (event) {
    return event;
};


export default class DragEvent {
    static {
        this.types = types;
    }

    constructor() {
        this.start = $.proxy(this.start, this);
        this.over = $.proxy(this.over, this);
        this.end = $.proxy(this.end, this);
        this.onstart = this.onover = this.onend = $.noop;
    }
    start(event) {
        event = this.startFix(event);

        $document
            .on(types.over, this.over)
            .on(types.end, this.end);

        this.onstart(event);
        return false;
    }
    over(event) {
        event = this.overFix(event);
        this.onover(event);
        return false;
    }
    end(event) {
        event = this.endFix(event);

        $document
            .off(types.over, this.over)
            .off(types.end, this.end);

        this.onend(event);
        return false;
    }
    startFix(event) {
        event = getEvent(event);

        this.target = $(event.target);
        this.selectstart = function () {
            return false;
        };

        $document
            .on('selectstart', this.selectstart)
            .on('dblclick', this.end);

        if (isLosecapture) {
            this.target.on('losecapture', this.end);
        } else {
            $window.on('blur', this.end);
        }

        if (isSetCapture) {
            this.target[0].setCapture();
        }

        return event;
    }
    overFix(event) {
        event = getEvent(event);
        return event;
    }
    endFix(event) {
        event = getEvent(event);

        $document
            .off('selectstart', this.selectstart)
            .off('dblclick', this.end);

        if (isLosecapture) {
            this.target.off('losecapture', this.end);
        } else {
            $window.off('blur', this.end);
        }

        if (isSetCapture) {
            this.target[0].releaseCapture();
        }

        return event;
    }
    /**
     * 启动拖拽
     * @param   {HTMLElement}   被拖拽的元素
     * @param   {Event} 触发拖拽的事件对象。可选，若无则监听 elem 的按下事件启动
     */
    static create(elem, event) {
        var $elem = $(elem);
        var dragEvent = new DragEvent();
        var startType = DragEvent.types.start;
        var noop = function () { };
        var className = elem.className
            .replace(/^\s|\s.*/g, '') + '-drag-start';

        var minX;
        var minY;
        var maxX;
        var maxY;

        var api = {
            onstart: noop,
            onover: noop,
            onend: noop,
            off: function () {
                $elem.off(startType, dragEvent.start);
            }
        };


        dragEvent.onstart = function (event) {
            var isFixed = $elem.css('position') === 'fixed';
            var dl = $document.scrollLeft();
            var dt = $document.scrollTop();
            var w = $elem.width();
            var h = $elem.height();

            minX = 0;
            minY = 0;
            maxX = isFixed ? $window.width() - w + minX : $document.width() - w;
            maxY = isFixed ? $window.height() - h + minY : $document.height() - h;

            var offset = $elem.offset();
            var left = this.startLeft = isFixed ? offset.left - dl : offset.left;
            var top = this.startTop = isFixed ? offset.top - dt : offset.top;

            this.clientX = event.clientX;
            this.clientY = event.clientY;

            $elem.addClass(className);
            api.onstart.call(elem, event, left, top);
        };


        dragEvent.onover = function (event) {
            var left = event.clientX - this.clientX + this.startLeft;
            var top = event.clientY - this.clientY + this.startTop;
            var style = $elem[0].style;

            left = Math.max(minX, Math.min(maxX, left));
            top = Math.max(minY, Math.min(maxY, top));

            style.left = left + 'px';
            style.top = top + 'px';

            api.onover.call(elem, event, left, top);
        };


        dragEvent.onend = function (event) {
            var position = $elem.position();
            var left = position.left;
            var top = position.top;
            $elem.removeClass(className);
            api.onend.call(elem, event, left, top);
        };


        dragEvent.off = function () {
            $elem.off(startType, dragEvent.start);
        };


        if (event) {
            dragEvent.start(event);
        } else {
            $elem.on(startType, dragEvent.start);
        }

        return api;
    }
}